/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.formserver.viewmodel.common;

import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bef.bizentity.beenum.BEOperationType;
import com.inspur.edp.bef.bizentity.operation.BizMgrAction;
import com.inspur.edp.bef.bizentity.operation.collection.BizMgrActionCollection;
import com.inspur.edp.bef.bizentity.operation.componentbase.BizParameter;
import com.inspur.edp.bef.bizentity.operation.componentbase.BizVoidReturnType;
import com.inspur.edp.bef.bizentity.operation.componentinterface.IBizParameter;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.collection.GspAssociationCollection;
import com.inspur.edp.cef.designtime.api.collection.GspAssociationKeyCollection;
import com.inspur.edp.cef.designtime.api.collection.GspEnumValueCollection;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspAssociationKey;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.designtime.api.element.GspEnumValue;
import com.inspur.edp.cef.designtime.api.util.Guid;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonModel;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.das.commonmodel.collection.GspUniqueConstraintCollection;
import com.inspur.edp.das.commonmodel.entity.GspCommonElement;
import com.inspur.edp.das.commonmodel.entity.element.ElementCodeRuleConfig;
import com.inspur.edp.das.commonmodel.entity.object.GspUniqueConstraint;
import com.inspur.edp.das.commonmodel.util.HandleAssemblyNameUtil;
import com.inspur.edp.das.commonmodel.util.MetadataProjectUtil;
import com.inspur.edp.formserver.viewmodel.DotNetToJavaStringHelper;
import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.GspViewModelElement;
import com.inspur.edp.formserver.viewmodel.GspViewObject;
import com.inspur.edp.formserver.viewmodel.action.MappedBizAction;
import com.inspur.edp.formserver.viewmodel.action.mappedbiz.MappedBizActionParameter;
import com.inspur.edp.formserver.viewmodel.action.viewmodelbase.ViewModelParameter;
import com.inspur.edp.formserver.viewmodel.action.viewmodelbase.ViewModelReturnValue;
import com.inspur.edp.formserver.viewmodel.action.viewmodelbase.ViewModelVoidReturnValue;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoElementMapping;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoElementSourceType;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoObjectMapping;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoObjectSourceType;
import com.inspur.edp.formserver.viewmodel.exception.ViewModelException;
import com.inspur.edp.formserver.viewmodel.exception.VoModelErrorCodes;
import io.iec.edp.caf.commons.utils.CollectionUtils;
import lombok.var;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * The Tool Of Convert Between Busiess Entity And  View Model
 *
 * @author Benjamin Gong
 * @version 2021/1/11 17:13
 */
public final class ConvertUtils {
    private static final String AUTO_GEN_VM_PREFIX = "GenVo";
    private static final String AUTO_GEN_VM_SUFFIX = "Service";

    public static GspViewModel convertToViewModel(GspBusinessEntity be, String pkgName, String metadataId) {
        return convertToViewModel(be, pkgName, metadataId, "");
    }

    public static GspViewModel convertToViewModel(GspBusinessEntity be, String pkgName, String metadataId, String voGeneratingAssembly) {
        //① CM结构构造
        GspViewModel vm = convertToGspViewModel(be, pkgName, metadataId, voGeneratingAssembly, null);
        vm.getMapping().setMapType(MappingType.BizEntity);
        //② BeMgrAction构造
        var beMgrActions = be.getCustomMgrActions(); //be.GetCustomMgrActions();
        buildVmActions(beMgrActions, vm, pkgName, metadataId);

        vm.setIsUseNamespaceConfig(true);
        return vm;
    }

    public static GspViewModel convertToViewModel(GspBusinessEntity be, String pkgName, String metadataId, String voGeneratingAssembly, String relativePath) {
        //① CM结构构造
        GspViewModel vm = convertToGspViewModel(be, pkgName, metadataId, voGeneratingAssembly, relativePath);
        vm.getMapping().setMapType(MappingType.BizEntity);
        //② BeMgrAction构造
        var beMgrActions = be.getCustomMgrActions(); //be.GetCustomMgrActions();
        buildVmActions(beMgrActions, vm, pkgName, metadataId);

        vm.setIsUseNamespaceConfig(true);
        return vm;
    }

    private static GspViewModel convertToGspViewModel(IGspCommonModel cm, String pkgName, String metadataId, String voGeneratingAssembly, String relativePath) {
        GspViewModel vm = new GspViewModel();
        //① BasicInfo
        convertBasicInfo(cm, vm, pkgName, metadataId, voGeneratingAssembly, relativePath);
        //② MainObject
        vm.setMainObject(toObject(cm.getMainObject(), pkgName, metadataId, "", GspVoObjectSourceType.BeObject));
        //③ BelongModel递归赋值
        vm.getMainObject().setBelongModel(vm);
        vm.getMainObject().setBelongModelID(vm.getID());
        setChildObjectBelongModel(vm.getMainObject(), vm);
        return vm;
    }


    /**
     * 设置子对象的所属模型属性
     *
     * @param parentObject 父对象
     * @param cm           模型
     */
    private static void setChildObjectBelongModel(IGspCommonObject parentObject, IGspCommonModel cm) {
        if (parentObject == null || parentObject.getContainChildObjects() == null || parentObject.getContainChildObjects().size() == 0) {
            return;
        }
        for (IGspCommonObject childObject : parentObject.getContainChildObjects()) {
            childObject.setBelongModel(cm);
            childObject.setBelongModelID(cm.getID());
            // 递归
            setChildObjectBelongModel(childObject, cm);
        }
    }

    private static void convertBasicInfo(IGspCommonModel cm, GspViewModel vm, String pkgName, String metadataId, String voGeneratingAssembly, String relativePath) {
        vm.setID(Guid.newGuid().toString());
        vm.setCode(String.format("%1$s%2$s", cm.getCode(), AUTO_GEN_VM_SUFFIX));
        vm.setName(String.format("%1$s_%2$s", AUTO_GEN_VM_PREFIX, cm.getName()));
        vm.setSimplifyGen(true);
        vm.setIsVirtual(false); //be带出，IsVirtual=false
        //vm.PrimayKeyID = cm.PrimayKeyID;
        //vm.EntityType = cm.ExtendType;
        vm.setEntityType(cm.getEntityType());
        String packagePrefix = null;
        if (relativePath != null) {
            packagePrefix = MetadataProjectUtil.getPackagePrefix(relativePath);
        }
        // todo:时区，临时屏蔽
//        vm.setEnableStdTimeFormat(true);
        //vm.ExtendNodeList = cm.ExtendNodeList;
//        if (cm.getFkConstraints() != null && cm.getFkConstraints().size() > 0) {
//            //vm.FkConstraints.addRange(cm.FkConstraints);
//        }
        if (DotNetToJavaStringHelper.isNullOrEmpty(voGeneratingAssembly)) {
            vm.setDotnetGeneratingAssembly(cm.getDotnetGeneratingAssembly() + ".Vo");
            vm.setGeneratingAssembly(HandleAssemblyNameUtil.convertToJavaPackageName(packagePrefix, cm.getDotnetGeneratingAssembly() + ".Vo"));
        } else {
            vm.setDotnetGeneratingAssembly(voGeneratingAssembly + ".Vo");
            vm.setGeneratingAssembly(HandleAssemblyNameUtil.convertToJavaPackageName(packagePrefix, voGeneratingAssembly + ".Vo"));
        }
        ViewModelMapping tempVar = new ViewModelMapping();
        tempVar.setMapType(MappingType.BizEntity);
        tempVar.setTargetMetadataPkgName(pkgName);
        tempVar.setTargetMetadataId(metadataId);
        tempVar.setTargetObjId(cm.getID());
        vm.setMapping(tempVar);
    }

    public static void dealVmBasicInfo(IGspCommonModel cm, GspViewModel vm, String pkgName, String metadataId, String voGeneratingAssembly) {
        vm.setID(Guid.newGuid().toString());
        vm.setCode(cm.getCode());
        vm.setName(cm.getName());
        vm.setSimplifyGen(true);
        vm.setIsVirtual(cm.getIsVirtual());
        vm.setEntityType(cm.getEntityType());
        if (DotNetToJavaStringHelper.isNullOrEmpty(voGeneratingAssembly)) {
            vm.setDotnetGeneratingAssembly(cm.getDotnetGeneratingAssembly() + ".Vo");
            vm.setGeneratingAssembly(HandleAssemblyNameUtil.convertToJavaPackageName(cm.getDotnetGeneratingAssembly() + ".Vo"));
        } else {
            vm.setDotnetGeneratingAssembly(voGeneratingAssembly + ".Vo");
            vm.setGeneratingAssembly(HandleAssemblyNameUtil.convertToJavaPackageName(voGeneratingAssembly + ".Vo"));
        }
        ViewModelMapping tempVar = new ViewModelMapping();
        tempVar.setMapType(MappingType.BizEntity);
        tempVar.setTargetMetadataPkgName(pkgName);
        tempVar.setTargetMetadataId(metadataId);
        tempVar.setTargetObjId(cm.getID());
        vm.setMapping(tempVar);
    }

    //    //#region Object
    public static GspViewObject toObject(IGspCommonObject cmObject, String pkgName, String metadataId, GspViewObject parentVoObj, GspVoObjectSourceType sourceType) {
        GspViewObject vmObject = new GspViewObject();

        //① BasicInfo
        convertObjectBasicInfo(cmObject, vmObject, pkgName, metadataId, sourceType);
        //② Element
        convertContainElements(cmObject, vmObject, pkgName, metadataId, sourceType);
        //③ 更新字段相关的属性（分级信息、ID字段等）
        // QO无相关属性，故不需要传入sourceType区分qo源
        convertObjectSelfInfo(cmObject, vmObject, parentVoObj, sourceType);
        //⑤ ChildObject
        // QO无子节点
        convertChildObjects(cmObject, vmObject, pkgName, metadataId);

        return vmObject;
    }

    public static GspViewObject toObject(IGspCommonObject cmObject, String pkgName, String metadataId, String parentObjIDElementId, GspVoObjectSourceType sourceType) {
        GspViewObject vmObject = new GspViewObject();

        //① BasicInfo
        convertObjectBasicInfo(cmObject, vmObject, pkgName, metadataId, sourceType);
        //② Element
        convertContainElements(cmObject, vmObject, pkgName, metadataId, sourceType);
        //③ 更新字段相关的属性（分级信息、ID字段等）
        // QO无相关属性，故不需要传入sourceType区分qo源
        convertObjectSelfInfo(cmObject, vmObject, parentObjIDElementId, sourceType);
        //⑤ ChildObject
        // QO无子节点
        convertChildObjects(cmObject, vmObject, pkgName, metadataId);

        return vmObject;
    }

    @Deprecated
    public static GspViewObject transToObject(IGspCommonObject cmObject, String pkgName, String metadataId, String parentObjIDElementId, GspVoObjectSourceType sourceType, List<String> fieldCodes) {
        GspViewObject vmObject = new GspViewObject();

        convertObjectBasicInfo(cmObject, vmObject, pkgName, metadataId, sourceType);

        convertContainElements(cmObject, vmObject, pkgName, metadataId, sourceType, fieldCodes);

        convertObjectSelfInfo(cmObject, vmObject, parentObjIDElementId, sourceType);

        convertChildObjects(cmObject, vmObject, pkgName, metadataId);

        return vmObject;
    }

    //
//
    private static void convertObjectBasicInfo(IGspCommonObject cmObject, GspViewObject vmObject, String pkgName, String metadataId, GspVoObjectSourceType sourceType) {
        dealObjectBasicInfo(cmObject, vmObject, pkgName, metadataId, sourceType);

        // 源相关属性
        convertObjSourceTypeRelatedProp(cmObject, vmObject, sourceType);
    }

    private static void dealObjectBasicInfo(IGspCommonObject cmObject, GspViewObject vmObject, String pkgName, String metadataId, GspVoObjectSourceType sourceType) {
        vmObject.setID(Guid.newGuid().toString());
        vmObject.setCode(cmObject.getCode());
        vmObject.setName(cmObject.getName());
        vmObject.setObjectType(cmObject.getObjectType());
        vmObject.setRefObjectName(cmObject.getRefObjectName());
        vmObject.setOrderbyCondition(cmObject.getOrderbyCondition());
        vmObject.setFilterCondition(cmObject.getFilterCondition());
        vmObject.setIsReadOnly(cmObject.getIsReadOnly());
        if (cmObject.getKeys() != null && cmObject.getKeys().size() > 0) {
            for (int i = 0; i < cmObject.getKeys().size(); i++) {
                vmObject.getKeys().add(cmObject.getKeys().get(i).clone());
            }
        }
        vmObject.setIsRef(false);
        GspVoObjectMapping tempVar = new GspVoObjectMapping();
        tempVar.setMapType(MappingType.BEObject);
        tempVar.setTargetMetadataPkgName(pkgName);
        tempVar.setTargetMetadataId(metadataId);
        tempVar.setTargetObjId(cmObject.getID());
        tempVar.setSourceType(sourceType);
        vmObject.setMapping(tempVar);
    }

    private static void convertObjSourceTypeRelatedProp(IGspCommonObject cmObj, GspViewObject vmObject, GspVoObjectSourceType sourceType) {
        switch (sourceType) {
            case BeObject:
            case QoObject:
                vmObject.setIsVirtual(false); //be带出，IsVirtual=false
                break;
            case VoObject:
                vmObject.setIsVirtual(cmObj.getIsVirtual());

                var originVo = (GspViewObject) cmObj;
                vmObject.setIsBeckendOnly(originVo.getIsBeckendOnly());
                vmObject.setPrimaryKey(originVo.getPrimaryKey()); // 无用
                vmObject.setDefaultPageSize(originVo.getDefaultPageSize());
                vmObject.setExtendProperties(originVo.getExtendProperties());
                break;
            default:
                throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1003, null, sourceType.toString());
        }
    }


    //#region 字段相关属性，需额外处理

    /**
     * vo对象转换时，更新字段相关属性
     */
    private static void convertObjectSelfInfo(IGspCommonObject cmObject, GspViewObject vmObject, GspViewObject parentVoObj, GspVoObjectSourceType objSourceType) {

        List<IGspCommonField> elementList = vmObject.getContainElements().stream().filter(item -> !item.getIsVirtual()).collect(Collectors.toList());
        Map<String, IGspCommonField> viewElements = elementList.stream().collect(Collectors.toMap(IGspCommonField::getID, item -> item));
        java.util.HashMap<String, String> elementMappings = getElementMappingsDic(elementList);

        //① ID字段和Keys更新
        updateIDElement(cmObject, vmObject);
        updateViewObjectKeys(cmObject, vmObject, parentVoObj);
        //② 分级信息HirachyInfo
//        GspVoElementSourceType eleSourceType = GetEleSourceType(objSourceType);
//        updateHirarchyInfo(cmObject, vmObject, elementMappings, viewElements, eleSourceType);
        //③ 列生成规则ColumnGenerateID
        updateColumnGenerateId(cmObject, vmObject, elementMappings);
        //④ 唯一性约束ContainConstraints
        updateContainConstraints(cmObject, vmObject, elementMappings, viewElements);
//        //⑤	时间戳(创建人等信息)
//        updateTimeStampElements(cmObject, vmObject, elementMappings);
        //⑥ 状态字段
        updateStateElementId(cmObject, vmObject, elementMappings);

    }

    private static void convertObjectSelfInfo(IGspCommonObject cmObject, GspViewObject vmObject, String parentObjIDElementId, GspVoObjectSourceType objSourceType) {

        List<IGspCommonField> elementList = vmObject.getContainElements().stream().filter(item -> !item.getIsVirtual()).collect(Collectors.toList());
        Map<String, IGspCommonField> viewElements = elementList.stream().collect(Collectors.toMap(IGspCommonField::getID, item -> item));
        java.util.HashMap<String, String> elementMappings = getElementMappingsDic(elementList);

        //① ID字段和Keys更新
        updateIDElement(cmObject, vmObject);
        updateViewObjectKeys(cmObject, vmObject, parentObjIDElementId);
        //② 分级信息HirachyInfo
//        GspVoElementSourceType eleSourceType = GetEleSourceType(objSourceType);
//        updateHirarchyInfo(cmObject, vmObject, elementMappings, viewElements, eleSourceType);
        //③ 列生成规则ColumnGenerateID
        updateColumnGenerateId(cmObject, vmObject, elementMappings);
        //④ 唯一性约束ContainConstraints
        updateContainConstraints(cmObject, vmObject, elementMappings, viewElements);
//        //⑤	时间戳(创建人等信息)
//        updateTimeStampElements(cmObject, vmObject, elementMappings);
        //⑥ 状态字段
        updateStateElementId(cmObject, vmObject, elementMappings);

    }

    /**
     * 更新状态字段
     *
     * @param beObject
     * @param viewObj
     * @param elementMappings
     */
    public static void updateStateElementId(IGspCommonObject beObject, GspViewObject viewObj, java.util.HashMap<String, String> elementMappings) {
        if (!DotNetToJavaStringHelper.isNullOrEmpty(beObject.getStateElementID()) && elementMappings.containsKey(beObject.getStateElementID())) {
            viewObj.setStateElementID(elementMappings.get(beObject.getStateElementID()));
        }
    }

    /**
     * 更新创建人等信息
     *
     * @param beObject
     * @param viewObj
     * @param elementMappings
     */
    public static void updateTimeStampElements(IGspCommonObject beObject, GspViewObject viewObj, java.util.HashMap<String, String> elementMappings) {

//        //！！！！爲啥是beObject.StateElementID，StateElementID不是狀態字段麽，時間戳字段用啥
//        if (!DotNetToJavaStringHelper.isNullOrEmpty(beObject.StateElementID) && elementMappings.containsKey(beObject.getCreatorElementID())) {
//            viewObj.CreatorElementID = elementMappings.get(beObject.CreatorElementID);
//        }
//        if (!DotNetToJavaStringHelper.isNullOrEmpty(beObject.StateElementID) && elementMappings.containsKey(beObject.CreatedDateElementID)) {
//            viewObj.CreatedDateElementID = elementMappings.get(beObject.CreatedDateElementID);
//        }
//        if (!DotNetToJavaStringHelper.isNullOrEmpty(beObject.StateElementID) && elementMappings.containsKey(beObject.ModifierElementID)) {
//            viewObj.ModifierElementID = elementMappings.get(beObject.ModifierElementID);
//        }
//        if (!DotNetToJavaStringHelper.isNullOrEmpty(beObject.StateElementID) && elementMappings.containsKey(beObject.ModifiedDateElementID)) {
//            viewObj.ModifiedDateElementID = elementMappings.get(beObject.ModifiedDateElementID);
//        }
    }

    /**
     * 更新唯一性约束
     *
     * @param beObject
     * @param viewObj
     * @param viewElements
     * @param elementMappings
     */
    public static void updateContainConstraints(IGspCommonObject beObject, GspViewObject viewObj,
                                                Map<String, String> elementMappings,
                                                Map<String, IGspCommonField> viewElements) {
        if (CollectionUtils.isEmpty(beObject.getContainConstraints())) {
            return;
        }

        GspUniqueConstraintCollection voUniqueCons = null;
        if (viewObj.getContainConstraints() != null) {
            voUniqueCons = viewObj.getContainConstraints().clone();
            viewObj.getContainConstraints().clear();
        }

        for (GspUniqueConstraint beConstraint : beObject.getContainConstraints()) {
            GspUniqueConstraint viewModelConstraint = beConstraint.clone();
            // clone 不应异常, 冗余处理
            if (viewModelConstraint == null) {
                continue;
            }
            viewModelConstraint.getElementList().clear();
            for (String element : beConstraint.getElementList()) {
                if (!elementMappings.containsKey(element)) {
                    continue;
                }
                String vmElementId = elementMappings.get(element);
                if (DotNetToJavaStringHelper.isNullOrEmpty(vmElementId)) {
                    continue;
                }
                if (!viewElements.containsKey(vmElementId)) {
                    throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1004, null, vmElementId);
                }
                viewModelConstraint.getElementList().add(vmElementId);
            }
            if (viewObj.getContainConstraints() == null) {
                viewObj.setContainConstraints(new GspUniqueConstraintCollection());
            }
            if (voUniqueCons != null) {
                for (GspUniqueConstraint voCon : voUniqueCons) {
                    if (voCon.getCode().equals(viewModelConstraint.getCode())) {
                        viewModelConstraint.setI18nResourceInfoPrefix(voCon.getI18nResourceInfoPrefix());
                    }
                }
            }
            viewObj.getContainConstraints().add(viewModelConstraint);
        }
    }

    /**
     * 更新ID生成规则
     *
     * @param beObject
     * @param viewObj
     * @param elementMappings
     */
    public static void updateColumnGenerateId(IGspCommonObject beObject, GspViewObject viewObj, Map<String, String> elementMappings) {
        if (elementMappings.containsKey(beObject.getColumnGenerateID().getElementID())) {
            viewObj.getColumnGenerateID().setElementID(elementMappings.get(beObject.getColumnGenerateID().getElementID()));
        }
        viewObj.getColumnGenerateID().setGenerateType(beObject.getColumnGenerateID().getGenerateType());
    }


    /**
     * 字段Mapping字典
     *
     * @param elementList
     * @return
     */
    public static java.util.HashMap<String, String> getElementMappingsDic(List<IGspCommonField> elementList) {
        java.util.HashMap<String, String> elementMappings = new java.util.HashMap<String, String>();
        for (IGspCommonField item : elementList) {
            GspViewModelElement viewModelElement = (GspViewModelElement) item;
            if (viewModelElement != null && viewModelElement.getMapping() != null) {
                String key = viewModelElement.getMapping().getTargetObjId();
                String value = viewModelElement.getID();
                elementMappings.put(key, value);
            }
        }
        return elementMappings;
    }


    /**
     * 更新ID字段及Keys
     *
     * @param cmObject
     * @param vmObject
     */
    private static void updateIDElement(IGspCommonObject cmObject, GspViewObject vmObject) {
        // idEle
        String idElementId = cmObject.getIDElement().getLabelID();
        var vmIdEle = vmObject.getElementByLabelId(idElementId);
        vmObject.getColumnGenerateID().setElementID(vmIdEle.getID());
    }

    /**
     * 更新Keys
     *
     * @param cmObject
     * @param vmObject
     */
    private static void updateViewObjectKeys(IGspCommonObject cmObject, GspViewObject vmObject, GspViewObject parentVoObj) {
        // keys
        var keys = cmObject.getKeys();
        if (!keys.isEmpty()) {
            if (vmObject.getKeys().isEmpty()) {
                throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1005, null, cmObject.getCode(), cmObject.getName());
            }
            for (GspAssociationKey key : vmObject.getKeys()) {
                GspAssociationKey associationKey = getAssociationKeyBySourceElementId(keys, key.getSourceElement());
                key.setSourceElement(getVmElementByMappedBizEleId(vmObject, associationKey.getSourceElement()).getID());
                key.setTargetElement(getVmElementByMappedBizEleId(parentVoObj, associationKey.getTargetElement()).getID());
            }
        }
    }

    public static void updateViewObjectKeys(IGspCommonObject cmObject, GspViewObject vmObject, String parentObjIDElementId) {
        // keys
        var keys = cmObject.getKeys();
        if (!keys.isEmpty()) {
            if (vmObject.getKeys().isEmpty()) {
                throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1005, null, cmObject.getCode(), cmObject.getName());
            }
            for (GspAssociationKey key : vmObject.getKeys()) {
                String beSourceEleId = getAssociationKeyBySourceElementId(keys, key.getSourceElement()).getSourceElement();
                key.setSourceElement(getVmElementByMappedBizEleId(vmObject, beSourceEleId).getID());
                key.setTargetElement(parentObjIDElementId);
            }
        }
    }

    private static GspCommonElement getVmElementByMappedBizEleId(GspViewObject vmObject, String bizEleId) {
        ArrayList<IGspCommonElement> vmElements = vmObject != null ? vmObject.getAllElementList(true): null;
        if (CollectionUtils.isEmpty(vmElements)) {
            throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1006, null, bizEleId);
        }
        for (IGspCommonElement item : vmElements) {
            if (bizEleId.equals(((GspViewModelElement) item).getMapping().getTargetObjId())) {
                return (GspCommonElement) item;
            }
        }
        throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1006, null, bizEleId);
    }

    private static GspAssociationKey getAssociationKeyBySourceElementId(GspAssociationKeyCollection keys, String sourceEleId) {
        if (keys.size() == 0) {
            throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1057, null);
        }
        for (GspAssociationKey item : keys) {
            if (sourceEleId.equals(item.getSourceElement())) {
                return item;
            }
        }
        throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1057, null);
    }
    //#endregion

    /**
     * 更新分级信息
     *
     * @param cmObject
     * @param vmObject
     * @param viewElements
     * @param elementMappings
     */

    public static void updateHirarchyInfo(IGspCommonObject cmObject, GspViewObject vmObject, java.util.HashMap<String, String> elementMappings, java.util.HashMap<String, IGspCommonElement> viewElements, GspVoElementSourceType eleSourceType) {

    }

    private static void convertChildObjects(IGspCommonObject cmObject, GspViewObject vmObject, String pkgName, String metadataId) {

        for (var childObject : cmObject.getContainChildObjects()) {
            GspViewObject cmChildObject = toObject(childObject, pkgName, metadataId, vmObject, GspVoObjectSourceType.BeObject);
            cmChildObject.setParentObject(vmObject);
            vmObject.getContainChildObjects().add(cmChildObject);
        }
    }

    //
    private static void convertContainElements(IGspCommonObject cmObject, GspViewObject vmObject, String pkgName, String metadataId, GspVoObjectSourceType objSourceType) {
        if (cmObject.getContainElements() == null || cmObject.getContainElements().size() < 1) {
            return;
        }
        GspVoElementSourceType eleSourceType = getEleSourceType(objSourceType);

        for (var cmElement : cmObject.getContainElements()) {
            GspViewModelElement element = toElement((GspCommonElement) cmElement, pkgName, metadataId, eleSourceType);
            element.setBelongObject(vmObject);
            vmObject.getContainElements().add(element);
        }
    }

    private static void convertContainElements(IGspCommonObject cmObject, GspViewObject vmObject, String pkgName, String metadataId, GspVoObjectSourceType objSourceType, List<String> fieldCodes) {
        if (cmObject.getContainElements() == null || cmObject.getContainElements().size() < 1) {
            return;
        }
        GspVoElementSourceType eleSourceType = getEleSourceType(objSourceType);
        if (fieldCodes == null || fieldCodes.size() == 0) {
            for (var cmElement : cmObject.getContainElements()) {
                GspViewModelElement element = toElement((GspCommonElement) cmElement, pkgName, metadataId, eleSourceType);
                element.setBelongObject(vmObject);
                vmObject.getContainElements().add(element);
            }
            return;
        }
        IGspCommonField iGspCommonField = cmObject.getContainElements().getByLabelId("ID");
        GspViewModelElement viewModelEle = toElement((GspCommonElement) iGspCommonField, pkgName, metadataId, eleSourceType);
        viewModelEle.setBelongObject(vmObject);
        vmObject.getContainElements().add(viewModelEle);
        for (var field : cmObject.getContainElements()) {
            if (!fieldCodes.contains(field.getCode()))
                continue;
            GspViewModelElement element = toElement((GspCommonElement) field, pkgName, metadataId, eleSourceType);
            element.setBelongObject(vmObject);
            vmObject.getContainElements().add(element);
        }
    }

    private static GspVoElementSourceType getEleSourceType(GspVoObjectSourceType objSourceType) {
        switch (objSourceType) {
            case BeObject:
                return GspVoElementSourceType.BeElement;
            case QoObject:
                return GspVoElementSourceType.QoElement;
            case VoObject:
                return GspVoElementSourceType.VoElement;
            default:
                throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1003, null, objSourceType.toString());
        }
    }

    //#endregion

    //#region Element
    public static GspViewModelElement toElement(IGspCommonElement element, String pkgName, String metadataId) {
        return toElement(element, pkgName, metadataId, GspVoElementSourceType.BeElement);
    }

    public static GspViewModelElement toElement(IGspCommonElement element, String pkgName, String metadataId, GspVoElementSourceType sourceType) {
        GspViewModelElement vmElement = new GspViewModelElement();

        //① 字段基础信息
        convertElementBasicInfo(element, vmElement, pkgName, metadataId, sourceType);
        //② 关联
        if (vmElement.getObjectType() == GspElementObjectType.Association) {
            convertAssociation(element, vmElement, pkgName, metadataId, sourceType);
        }
        //③ 枚举
        if (vmElement.getObjectType() == GspElementObjectType.Enum) {
            convertEnumInfo(element, vmElement);
        }
        return vmElement;
    }

    private static void convertElementBasicInfo(IGspCommonElement cmEle, GspViewModelElement vmEle, String pkgName, String metadataId, GspVoElementSourceType sourceType) {
        dealElementBasicInfo(cmEle, vmEle, pkgName, metadataId, sourceType);
        // 字段源不同，进行不同处理的属性
        convertEleSourceTypeRelatedProp(cmEle, vmEle, sourceType);
        // 动态属性
        vmEle.setDynamicPropSetInfo(cmEle.getDynamicPropSetInfo());
    }

    private static void dealElementBasicInfo(IGspCommonElement cmEle, GspViewModelElement vmEle, String pkgName, String metadataId, GspVoElementSourceType sourceType) {
        vmEle.setID(Guid.newGuid().toString());
        vmEle.setCode(cmEle.getCode());
        vmEle.setName(cmEle.getName());
        vmEle.setLabelID(cmEle.getLabelID());
        vmEle.setMDataType(cmEle.getMDataType());
        vmEle.setLength(cmEle.getLength());
        vmEle.setPrecision(cmEle.getPrecision());
        vmEle.setObjectType(cmEle.getObjectType());
        vmEle.setBelongModelID(cmEle.getBelongModelID());
        vmEle.setIsMultiLanguage(cmEle.getIsMultiLanguage());
        vmEle.setRefElementId(cmEle.getRefElementId());
        vmEle.setIsCustomItem(cmEle.getIsCustomItem());
        vmEle.setIsRequire(cmEle.getIsRequire());
        vmEle.setIsRefElement(cmEle.getIsRefElement());
        vmEle.setReadonly(cmEle.getReadonly());
        vmEle.setIsFromAssoUdt(cmEle.getIsFromAssoUdt());
        vmEle.setEnumIndexType(cmEle.getEnumIndexType());
        vmEle.setIsUdt(cmEle.getIsUdt());
        vmEle.setRefBusinessFieldId(cmEle.getRefBusinessFieldId());
        vmEle.setIsMultiAssociation(cmEle.isMultiAssociation());
        //业务字段相关属性
        if (vmEle.getIsUdt()) {
            convertUdtInfo(cmEle, vmEle);
        }

        GspVoElementMapping tempVar = new GspVoElementMapping();
        tempVar.setMapType(MappingType.Element);
        tempVar.setTargetMetadataId(metadataId);
        tempVar.setTargetMetadataPkgName(pkgName);
        tempVar.setTargetObjId(cmEle.getID());
        tempVar.setTargetElementId(cmEle.getID());
        tempVar.setTargetObjectId(cmEle.getBelongObject() == null ? null : cmEle.getBelongObject().getID());
        tempVar.setSourceType(sourceType);
        vmEle.setMapping(tempVar);
    }

    private static void convertEleSourceTypeRelatedProp(IGspCommonElement cmEle, GspViewModelElement vmEle, GspVoElementSourceType sourceType) {
        switch (sourceType) {
            case BeElement:
            case QoElement:
                //vmEle.setColumnID(cmEle.getColumnID());
                //vmEle.setDefaultValue(cmEle.getDefaultValue()); // 20180921_测试用例评审_默认值不带出
                //convertBillCodeConfig(vmEle.BillCodeConfig, cmEle.BillCodeConfig);
                //vmEle.setDefaultValueType(cmEle.getDefaultValueType());
                vmEle.setIsVirtualViewElement(false);
                vmEle.setIsVirtual(cmEle.getIsVirtual()); //be带出，IsVirtual=false
                vmEle.setIsRef(cmEle.getIsRef());
                break;
            case VoElement:
                vmEle.setColumnID(cmEle.getColumnID());
                vmEle.setDefaultValue(cmEle.getDefaultValue());
                convertBillCodeConfig(vmEle.getBillCodeConfig(), cmEle.getBillCodeConfig());
                vmEle.setDefaultValueType(cmEle.getDefaultValueType());

                vmEle.setIsVirtual(cmEle.getIsVirtual());
                vmEle.setIsRef(cmEle.getIsRef());


                GspViewModelElement sourceVoEle = (GspViewModelElement) cmEle;
                vmEle.setIsVirtualViewElement(sourceVoEle.getIsVirtualViewElement());
                vmEle.setIsBeckendOnly(sourceVoEle.getIsBeckendOnly());
                vmEle.setImmediateSubmission(sourceVoEle.getImmediateSubmission());
                vmEle.setShowInFilter(sourceVoEle.getShowInFilter());
                vmEle.setShowInSort(sourceVoEle.getShowInSort());
                vmEle.setVMHelpConfig(sourceVoEle.getVMHelpConfig());
                vmEle.setHelpActions(sourceVoEle.getHelpActions());
                vmEle.setExtendProperties(sourceVoEle.getExtendProperties());
                vmEle.setExtendProperties(sourceVoEle.getExtendProperties());
                break;
            default:
                throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1007, null, sourceType.toString());
        }
    }


    private static void convertBillCodeConfig(ElementCodeRuleConfig config, ElementCodeRuleConfig sourceConfig) {
        config.setCanBillCode(sourceConfig.getCanBillCode());
        config.setBillCodeID(sourceConfig.getBillCodeID());
        config.setBillCodeName(sourceConfig.getBillCodeID());
        config.setCodeGenerateType(sourceConfig.getCodeGenerateType());
        config.setCodeGenerateOccasion(sourceConfig.getCodeGenerateOccasion());
    }

    private static void convertEnumInfo(IGspCommonElement cmEle, GspViewModelElement vmEle) {
        // 关联带出字段，无关联信息
        if (cmEle.getIsRefElement()) {
            return;
        }
        if (cmEle.getContainEnumValues() == null || cmEle.getContainEnumValues().size() < 1) {
            throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1009, null, cmEle.getCode(), cmEle.getName());
        }
        vmEle.setContainEnumValues(new GspEnumValueCollection());
        for (GspEnumValue enumValue : cmEle.getContainEnumValues()) {
            vmEle.getContainEnumValues().add(enumValue.clone());
        }
    }


    private static void convertUdtInfo(IGspCommonElement cmEle, GspViewModelElement vmEle) {
        vmEle.setUdtID(cmEle.getUdtID());
        vmEle.setUdtName(cmEle.getUdtName());
        vmEle.setUdtPkgName(cmEle.getUdtPkgName());
        //TODO 后续UDt中集成VM属性后，需要获取UDT后赋值
    }

    //#region 关联
    private static void convertAssociation(IGspCommonElement cmEle, GspViewModelElement vmEle, String pkgName, String metadataId, GspVoElementSourceType sourceType) {
        // 关联带出字段，无关联信息
        if (cmEle.getIsRefElement()) {
            return;
        }
        if (cmEle.getChildAssociations() == null || cmEle.getChildAssociations().size() < 1) {
            throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1010, null, cmEle.getCode(), cmEle.getName());
        }
        vmEle.setChildAssociations(new GspAssociationCollection());
        for (GspAssociation cmAssociation : cmEle.getChildAssociations()) {
            GspAssociation vmAssociation = getAssociation(cmAssociation, pkgName, metadataId, vmEle.getID(), sourceType);
            if (vmAssociation == null) {
                continue;
            }
            vmAssociation.setBelongElement(vmEle);
            vmEle.getChildAssociations().add(vmAssociation);
        }
    }

    private static GspAssociation getAssociation(GspAssociation cmAssociation, String pkgName, String metadataId, String vmElementId, GspVoElementSourceType souceType) {
        GspAssociation vmAssociation = cmAssociation.clone();

        if (vmAssociation == null) {
            return null;
        }

        vmAssociation.getKeyCollection().clear();
        for (GspAssociationKey associationKey : cmAssociation.getKeyCollection()) {
            GspAssociationKey voKey = associationKey.clone();
            voKey.setTargetElement(vmElementId);
            vmAssociation.getKeyCollection().add(voKey);
        }
        vmAssociation.getRefElementCollection().clear();

        for (IGspCommonField refElement : cmAssociation.getRefElementCollection()) {
            GspViewModelElement vmRefElement = new GspViewModelElement();
            convertElementBasicInfo((IGspCommonElement) refElement, vmRefElement, pkgName, metadataId, souceType);
            if (refElement.getObjectType() == GspElementObjectType.Enum) {
                convertEnumInfo((IGspCommonElement) refElement, vmRefElement);
            }
            vmRefElement.setParentAssociation(vmAssociation);
            vmAssociation.getRefElementCollection().add(vmRefElement);
        }


        return vmAssociation;
    }

    private static void buildVmActions(BizMgrActionCollection beMgrActions, GspViewModel vm, String pkgName, String metadataId) {
        if (beMgrActions == null) {
            return;
        }

        for (var bizMgrAction : beMgrActions) {
            if (bizMgrAction.getOpType() == BEOperationType.BizMgrAction) {
                MappedBizAction vmAction = toMappedAction((BizMgrAction) bizMgrAction, metadataId, pkgName);
                vm.getActions().add(vmAction);
            }
        }
    }

    public static MappedBizAction toMappedAction(BizMgrAction bizMgrAction, String metadataId, String pkgName) {
        MappedBizAction tempVar = new MappedBizAction();
        tempVar.setID(Guid.newGuid().toString());
        tempVar.setCode(bizMgrAction.getCode());
        tempVar.setName(bizMgrAction.getName());
        tempVar.setReturnValue(getReturnValue(bizMgrAction));
        tempVar.setComponentName(bizMgrAction.getComponentName());
        ViewModelMapping mapping = new ViewModelMapping();
        mapping.setMapType(MappingType.BizOperation);
        mapping.setTargetMetadataId(metadataId);
        mapping.setTargetMetadataPkgName(pkgName);
        mapping.setTargetObjId(bizMgrAction.getID());
        tempVar.setMapping(mapping);
        MappedBizAction vmAction = tempVar;
        initParameterCollection(vmAction, bizMgrAction);
        return vmAction;
    }

    private static void initParameterCollection(MappedBizAction vmAction, BizMgrAction action) {
        if (action.getParameters() == null || action.getParameters().getCount() < 1) {
            return;
        }
        for (int i = 0; i < action.getParameters().getCount(); i++) {
            MappedBizActionParameter param = new MappedBizActionParameter();
            transViewModelParameter(param, action.getParameters().getItem(i));
            vmAction.getParameterCollection().add(param);
        }
    }


    private static ViewModelReturnValue getReturnValue(BizMgrAction action) {
        if (action.getReturnValue() == null || action.getReturnValue() instanceof BizVoidReturnType) {
            return new ViewModelVoidReturnValue();
        }
        ViewModelReturnValue returnValue = new ViewModelReturnValue();
        transViewModelParameter(returnValue, action.getReturnValue());
        return returnValue;
    }

    private static void transViewModelParameter(ViewModelParameter vmParam, IBizParameter param) {
        vmParam.setID(param.getID());
        vmParam.setAssembly(param.getAssembly());
        vmParam.setClassName(param.getClassName());
        vmParam.setDotnetClassName(((BizParameter) param).getNetClassName());
        switch (param.getMode()) {
            case IN:
                vmParam.setMode(VMParameterMode.IN);
                break;
            case INOUT:
                vmParam.setMode(VMParameterMode.INOUT);
                break;
            case OUT:
                vmParam.setMode(VMParameterMode.OUT);
                break;
        }
        switch (param.getCollectionParameterType()) {
            case None:
                vmParam.setCollectionParameterType(VMCollectionParameterType.None);
                break;
            case List:
                vmParam.setCollectionParameterType(VMCollectionParameterType.List);
                break;
            case Array:
                vmParam.setCollectionParameterType(VMCollectionParameterType.Array);
                break;
        }
        vmParam.setParamCode(param.getParamCode());
        vmParam.setParamDescription(param.getParamDescription());
        vmParam.setParamName(param.getParamName());
        switch (param.getParameterType()) {
            case Boolean:
                vmParam.setParameterType(VMParameterType.Boolean);
                break;
            case Custom:
                vmParam.setParameterType(VMParameterType.Custom);
                break;
            case DateTime:
                vmParam.setParameterType(VMParameterType.DateTime);
                break;
            case Decimal:
                vmParam.setParameterType(VMParameterType.Decimal);
                break;
            case Double:
                vmParam.setParameterType(VMParameterType.Double);
                break;
            case Int32:
                vmParam.setParameterType(VMParameterType.Int32);
                break;
            case Object:
                vmParam.setParameterType(VMParameterType.Object);
                break;
            case String:
                vmParam.setParameterType(VMParameterType.String);
                break;
            case Stream:
                vmParam.setParameterType(VMParameterType.Stream);
                break;
            default:
                throw new ViewModelException(VoModelErrorCodes.GSP_VIEWOBJECT_MODEL_1008, null,
                        param.getParamCode(), param.getParamName(), param.getParameterType().toString());
        }
    }
}
